﻿using MetaProperties.Core;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MetaProperties.Framework;
#if ANCESTRY_SUPPORTED
using MetaProperties.Framework.Ancestry;
#endif
using Rhino.Mocks;

namespace MetaProperties.Core.Tests
{
    
    
    /// <summary>
    ///This is a test class for TypedObservableTest and is intended
    ///to contain all TypedObservableTest Unit Tests
    ///</summary>
    [TestClass()]
    public class TypedObservableTest
    {
        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        #region Additional test attributes
        // 
        //You can use the following additional attributes as you write your tests:
        //
        //Use ClassInitialize to run code before running the first test in the class
        //[ClassInitialize()]
        //public static void MyClassInitialize(TestContext testContext)
        //{
        //}
        //
        //Use ClassCleanup to run code after all tests in a class have run
        //[ClassCleanup()]
        //public static void MyClassCleanup()
        //{
        //}
        //
        //Use TestInitialize to run code before running each test
        //[TestInitialize()]
        //public void MyTestInitialize()
        //{
        //}
        //
        //Use TestCleanup to run code after each test has run
        //[TestCleanup()]
        //public void MyTestCleanup()
        //{
        //}
        //
        #endregion


        /// <summary>
        ///A test for MetaProperty
        ///</summary>
        public void MetaPropertyTestHelper<T>()
        {
            IObservable<T> container = new Observable<T>();
            PrivateObject param0 = new PrivateObject(container, new PrivateType(typeof(TypedObservable<T>)));
            TypedObservable_Accessor<T> target = new TypedObservable_Accessor<T>(param0);
            IMetaProperty<T> expected = new MetaProperty<T>(container);
            IMetaProperty<T> actual;
            target.MetaProperty = expected;
            actual = target.MetaProperty;
            Assert.AreEqual(expected, actual);
        }

        [TestMethod()]
        [DeploymentItem("MetaProperties.Core.dll")]
        public void MetaPropertyTest()
        {
            MetaPropertyTestHelper<GenericParameterHelper>();
        }

#if ANCESTRY_SUPPORTED
        /// <summary>
        ///A test for UpdateValueParent
        ///</summary>
        public void UpdateValueParentTestHelper()
        {
            MockRepository mocks = new MockRepository();
            IParentedMetaPropertyHost parentedValue = mocks.StrictMock<IParentedMetaPropertyHost>();
            
            IObservable<object> container = new Observable<object>();

            Expect.Call(() => parentedValue.UpdateParent(null, UpdateParentOperation.Add));

            mocks.ReplayAll();

            PrivateObject param0 = new PrivateObject(container, new PrivateType(typeof(TypedObservable<object>)));
            TypedObservable_Accessor<object> target = new TypedObservable_Accessor<object>(param0);
            object value = parentedValue;
            UpdateParentOperation operation = UpdateParentOperation.Add;

            target.UpdateValueParent(value, operation);

            mocks.VerifyAll();
        }

        [TestMethod()]
        [DeploymentItem("MetaProperties.Core.dll")]
        public void UpdateValueParentTest()
        {
            UpdateValueParentTestHelper();
        }
#endif

        /// <summary>
        ///A test for OnValueSet
        ///</summary>
        public void OnValueSetTestHelper<T>()
        {
            Observable<T> container = new Observable<T>();
            PrivateObject param0 = new PrivateObject(container, new PrivateType(typeof(TypedObservable<T>)));
            TypedObservable_Accessor<T> target = new TypedObservable_Accessor<T>(param0);
            T value = default(T);

            bool valueSetRaised = false;
            bool valueChangedRaised = false;
            bool propertyChangingRaised = false;
            bool propertyChangedRaised = false;
            bool untypedValueSetRaised = false;
            bool untypedValueChangedRaised = false;

            container.PropertyChanged += (s, e) =>
            {
                propertyChangedRaised = true;
            };

            container.PropertyChanging += (s, e) =>
            {
                propertyChangingRaised = true;
            };

            container.ValueSet += (s, e) =>
            {
                Assert.AreEqual(container, s);
                Assert.AreEqual(default(GenericParameterHelper), e.OldValue);
                Assert.AreEqual(value, e.NewValue);
                valueSetRaised = true;
            };

            container.ValueChanged += (s, e) =>
            {
                valueChangedRaised = true;
            };

            container.UntypedValueChanged += (s, e) =>
            {
                untypedValueChangedRaised = true;
            };

            container.UntypedValueSet += (s, e) =>
            {
                Assert.AreEqual(container, s);
                untypedValueSetRaised = true;
            };

            target.OnValueSet(value);

            Assert.IsFalse(propertyChangingRaised);
            Assert.IsFalse(propertyChangedRaised);
            Assert.IsTrue(valueSetRaised);
            Assert.IsFalse(valueChangedRaised);
            Assert.IsTrue(untypedValueSetRaised);
            Assert.IsFalse(untypedValueChangedRaised);
        }

        [TestMethod()]
        [DeploymentItem("MetaProperties.Core.dll")]
        public void OnValueSetTest()
        {
            OnValueSetTestHelper<GenericParameterHelper>();
        }


        /// <summary>
        /// A test for OnValueSet using only untyped event handlers,
        /// which tests some optimized code for creating event args only if required.
        /// </summary>
        public void OnValueSetUntypedTestHelper<T>()
        {
            Observable<T> container = new Observable<T>();
            PrivateObject param0 = new PrivateObject(container, new PrivateType(typeof(TypedObservable<T>)));
            TypedObservable_Accessor<T> target = new TypedObservable_Accessor<T>(param0);
            T value = default(T);

            bool untypedValueSetRaised = false;
            bool untypedValueChangedRaised = false;

            container.UntypedValueChanged += (s, e) =>
            {
                untypedValueChangedRaised = true;
            };

            container.UntypedValueSet += (s, e) =>
            {
                Assert.AreEqual(container, s);
                untypedValueSetRaised = true;
            };

            target.OnValueSet(value);

            Assert.IsTrue(untypedValueSetRaised);
            Assert.IsFalse(untypedValueChangedRaised);
        }

        [TestMethod()]
        [DeploymentItem("MetaProperties.Core.dll")]
        public void OnValueSetUntypedTest()
        {
            OnValueSetUntypedTestHelper<GenericParameterHelper>();
        }

        /// <summary>
        ///A test for OnValueChanging
        ///</summary>
        public void OnValueChangingTestHelper<T>()
        {
            Observable<T> container = new Observable<T>();
            PrivateObject param0 = new PrivateObject(container, new PrivateType(typeof(TypedObservable<T>)));
            TypedObservable_Accessor<T> target = new TypedObservable_Accessor<T>(param0);

            bool valueSetRaised = false;
            bool valueChangedRaised = false;
            bool propertyChangingRaised = false;
            bool propertyChangedRaised = false;
            bool untypedValueSetRaised = false;
            bool untypedValueChangedRaised = false;

            container.PropertyChanged += (s, e) =>
            {
                propertyChangedRaised = true;
            };

            container.PropertyChanging += (s, e) =>
            {
                Assert.AreEqual(container, s);
                Assert.AreEqual("Value", e.PropertyName);
                propertyChangingRaised = true;
            };

            container.ValueSet += (s, e) =>
            {
                valueSetRaised = true;
            };

            container.ValueChanged += (s, e) =>
            {
                valueChangedRaised = true;
            };

            container.UntypedValueChanged += (s, e) =>
            {
                untypedValueChangedRaised = true;
            };

            container.UntypedValueSet += (s, e) =>
            {
                untypedValueSetRaised = true;
            };

            target.OnValueChanging();

            Assert.IsTrue(propertyChangingRaised);
            Assert.IsFalse(propertyChangedRaised);
            Assert.IsFalse(valueSetRaised);
            Assert.IsFalse(valueChangedRaised);
            Assert.IsFalse(untypedValueSetRaised);
            Assert.IsFalse(untypedValueChangedRaised);
        }

        [TestMethod()]
        [DeploymentItem("MetaProperties.Core.dll")]
        public void OnValueChangingTest()
        {
            OnValueChangingTestHelper<GenericParameterHelper>();
        }

        /// <summary>
        ///A test for OnValueChanged
        ///</summary>
        public void OnValueChangedTestHelper<T>(T oldValue, T newValue)
        {
            Observable<T> container = new Observable<T>();
            PrivateObject param0 = new PrivateObject(container, new PrivateType(typeof(TypedObservable<T>)));
            TypedObservable_Accessor<T> target = new TypedObservable_Accessor<T>(param0);

            bool valueSetRaised = false;
            bool valueChangedRaised = false;
            bool propertyChangingRaised = false;
            bool propertyChangedRaised = false;
            bool untypedValueSetRaised = false;
            bool untypedValueChangedRaised = false;

            container.PropertyChanged += (s, e) =>
            {
                Assert.AreEqual(container, s);
                Assert.AreEqual("Value", e.PropertyName);
                propertyChangedRaised = true;
            };

            container.PropertyChanging += (s, e) =>
            {
                propertyChangingRaised = true;
            };

            container.ValueSet += (s, e) =>
            {
                Assert.AreEqual(container, s);
                Assert.AreEqual(oldValue, e.OldValue);
                Assert.AreEqual(newValue, e.NewValue);
                valueSetRaised = true;
            };

            container.ValueChanged += (s, e) =>
            {
                Assert.AreEqual(container, s);
                Assert.AreEqual(oldValue, e.OldValue);
                Assert.AreEqual(newValue, e.NewValue);
                valueChangedRaised = true;
            };

            container.UntypedValueChanged += (s, e) =>
            {
                Assert.AreEqual(container, s);
                untypedValueChangedRaised = true;
            };

            container.UntypedValueSet += (s, e) =>
            {
                Assert.AreEqual(container, s);
                untypedValueSetRaised = true;
            };

            target.OnValueChanged(oldValue, newValue);

            Assert.IsFalse(propertyChangingRaised);
            Assert.IsTrue(propertyChangedRaised);
            Assert.IsTrue(valueSetRaised);
            Assert.IsTrue(valueChangedRaised);
            Assert.IsTrue(untypedValueSetRaised);
            Assert.IsTrue(untypedValueChangedRaised);
        }

        [TestMethod()]
        [DeploymentItem("MetaProperties.Core.dll")]
        public void OnValueChangedTest()
        {
            OnValueChangedTestHelper<GenericParameterHelper>(new GenericParameterHelper(1), new GenericParameterHelper(2));
        }


        /// <summary>
        /// A test for OnValueChanged using only untyped event handlers,
        /// which tests some optimized code for creating event args only if required.
        /// </summary>
        public void OnValueChangedTestUntypedHelper<T>(T oldValue, T newValue)
        {
            Observable<T> container = new Observable<T>();
            PrivateObject param0 = new PrivateObject(container, new PrivateType(typeof(TypedObservable<T>)));
            TypedObservable_Accessor<T> target = new TypedObservable_Accessor<T>(param0);

            bool untypedValueSetRaised = false;
            bool untypedValueChangedRaised = false;

            container.UntypedValueChanged += (s, e) =>
            {
                Assert.AreEqual(container, s);
                untypedValueChangedRaised = true;
            };

            container.UntypedValueSet += (s, e) =>
            {
                Assert.AreEqual(container, s);
                untypedValueSetRaised = true;
            };

            target.OnValueChanged(oldValue, newValue);

            Assert.IsTrue(untypedValueSetRaised);
            Assert.IsTrue(untypedValueChangedRaised);
        }

        [TestMethod()]
        [DeploymentItem("MetaProperties.Core.dll")]
        public void OnValueChangedUntypedTest()
        {
            OnValueChangedTestUntypedHelper<GenericParameterHelper>(new GenericParameterHelper(1), new GenericParameterHelper(2));
        }

        /// <summary>
        ///A test for GetMetaProperty
        ///</summary>
        public void GetMetaPropertyTestHelper<T>()
        {
            Observable<T> container = new Observable<T>();
            PrivateObject param0 = new PrivateObject(container, new PrivateType(typeof(TypedObservable<T>)));
            TypedObservable_Accessor<T> target = new TypedObservable_Accessor<T>(param0);

            IMetaProperty actual;
            actual = target.GetMetaProperty();
            Assert.IsNotNull(actual);

            Assert.AreEqual(container.Value, actual.UntypedValue);
        }

        [TestMethod()]
        [DeploymentItem("MetaProperties.Core.dll")]
        public void GetMetaPropertyTest()
        {
            GetMetaPropertyTestHelper<GenericParameterHelper>();
        }

        [TestMethod()]
        [ExpectedException(typeof(InterfaceNotImplementedException))]
        public void IncorrectDerivedClassTest()
        {
            IncorrectDerived<GenericParameterHelper> test = new IncorrectDerived<GenericParameterHelper>();
        }

        /// <summary>
        /// A test implementation which doesn't implement IObservable&lt;T&gt;.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        private class IncorrectDerived<T> : TypedObservable<T>
        {
            protected override object GetValue()
            {
                throw new System.NotImplementedException();
            }
        }
    }
}
